﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Net.Sockets;

namespace SharpWebSocket.Protocol
{
    internal class Draft10
    {
        internal readonly static string MAGIC_KEY = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

        internal static Draft10HandShakeRequest BuildHandShakeRequest( Socket socket )
        {
            return new Draft10HandShakeRequest( socket );
        }

        internal static Draft10HandShakeRequest BuildHandShakeRequest( string receivedString )
        {
            return new Draft10HandShakeRequest( receivedString );
        }

        internal static Draft10HandShakeResponse BuildHandShakeResponse( Draft10HandShakeRequest request )
        {
            return new Draft10HandShakeResponse( request );
        }

        internal static SocketError Handshake( Socket clientSocket )
        {
            var request = Draft10.BuildHandShakeRequest( clientSocket );
            var response = Draft10.BuildHandShakeResponse( request );
            return response.CompleteHandShake( clientSocket );
        }

        internal static string WrapRequest( byte[] bytes, int readCount )
        {
            int i = 0;
            int len = 0;
            int start = 0;

            var data = bytes;

            int length = 0;
            byte[] maskData = new byte[0];

            byte[] frameData = new byte[0];

            if (data[0] == 0x81)
            {
                // 检查是否使用了掩码
                if (data[1] >= 0x80)
                {
                    // 数据长度不一样，掩码位置不一样
                    if (readCount < 0x84)
                    {
                        // firefox下，超过一个包长度的数据会被拆分为多个包
                        // 其中首包只包含头信息，第二个字节为0xfe
                        if (data[1] == 0xfe)
                        {
                            len = readCount;
                            // firefox
                            maskData = new byte[] { data[len - 4], data[len - 3], data[len - 2], data[len - 1] };
                            length = data[len - 5];

                            for (i = len - 6; i > 1; i--)
                            {
                                length += data[i] * (len - 5 - i) * 256;
                            }
                            Console.WriteLine( "firefox multi packages, length: " + length );
                        }
                        else
                        {
                            // chrome
                            length = data[1] - 0x80;
                            Console.WriteLine( "7bit, length: " + length.ToString() );
                            maskData = new byte[] { data[2], data[3], data[4], data[5] };
                            start = 6;
                        }
                    }
                    else if (readCount < 0xfe80)
                    {
                        length = data[2] * 256 + data[3];
                        Console.WriteLine( "7bit, length: " + length );
                        maskData = new byte[] { data[4], data[5], data[6], data[7] };
                        start = 8;
                    }
                    else
                    {
                        length = data[11];
                        for (i = 10; i > 3; i--)
                        {
                            length += data[i] * (11 - i) * 256;
                        }
                        Console.WriteLine( "7 + 64bit, length:" + length.ToString() );
                        maskData = new byte[] { data[12], data[13], data[14], data[15] };
                        start = 16;
                    }

                    using (MemoryStream ms = new MemoryStream())
                    {

                        for (i = start, len = readCount; i < len; i++)
                        {
                            ms.WriteByte( Convert.ToByte( maskData[(i - start) % 4] ^ data[i] ) );
                        }
                        frameData = ms.ToArray();
                    }
                    // Console.WriteLine( "data is :" + Encoding.UTF8.GetString( frameData ) );
                }
                else
                {
                    if (readCount < 0x80)
                    {
                        start = 2;
                    }
                    else if (readCount < 0xfe81)
                    {
                        start = 4;
                    }
                    else
                    {
                        start = 12;
                    }

                    frameData = new byte[readCount - start];
                    Buffer.BlockCopy( data, start, frameData, 0, data.Length - start );
                    Console.WriteLine( "1st packge frame length:" + frameData.Length );
                    if (frameData.Length == length)
                    {
                        Console.WriteLine( Encoding.UTF8.GetString( frameData ) );
                    }
                    frameData = new byte[0];
                    length = 0;
                }
                // return;
                return Encoding.UTF8.GetString( frameData );
            }
            
            // firefox 连续的数据包
            if (maskData.Length > 0)
            {
                MemoryStream ms = new MemoryStream();
                int l = 0;
                for (i = 0, l = readCount; i < l; i++)
                {
                    ms.WriteByte( Convert.ToByte( maskData[i % 4] ^ data[i] ) );
                }
                frameData = ms.ToArray();
                ms.Dispose();
                Console.WriteLine( "frame length:" + frameData.Length );

                if (frameData.Length == length)
                {
                    Console.WriteLine( Encoding.UTF8.GetString( frameData ) );
                }
                frameData = new byte[0];
                length = 0;
            }
            return Encoding.UTF8.GetString( frameData );
        }

        internal static byte[] WrapResponse( string data )
        {
            var bytes = Encoding.UTF8.GetBytes( data );
            return WrapResponse( bytes );
        }

        internal static byte[] WrapResponse( byte[] bytes )
        {
            byte[] frame = new byte[0];
            int length = bytes.Length; //Encoding.UTF8.GetBytes( data ).Length;

            if (length > 0x7D)
            {
                frame = new byte[]
                {
                    0x81,
                    0x7e,
                    Convert.ToByte( length >> 8 ),
                    Convert.ToByte( length & 0xFF ) // 1111 1111
                };
            }
            else
            {
                frame = new byte[]
                {
                    0x81,
                    Convert.ToByte( length )
                };
            }

            using (MemoryStream ms = new MemoryStream())
            {
                ms.Write( frame, 0, frame.Length );
                // var databytes = Encoding.UTF8.GetBytes( data );
                ms.Write( bytes, 0, bytes.Length );
                return ms.ToArray();
            }
        }

        
    }
}
